home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / gnu / nethack.lha / nethack-3.1 / src / options.c < prev    next >
C/C++ Source or Header  |  1993-01-25  |  27KB  |  1,087 lines

  1. /*    SCCS Id: @(#)options.c    3.1    92/11/14    */
  2. /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
  3. /* NetHack may be freely redistributed.  See license for details. */
  4.  
  5. #include "hack.h"
  6. #include "termcap.h"
  7. #include <ctype.h>
  8.  
  9. /*
  10.  *  NOTE:  If you add (or delete) an option, please update the short
  11.  *  options help (option_help()), the long options help (dat/opthelp),
  12.  *  and the current options setting display function (doset()).
  13.  */
  14.  
  15. #if defined(TOS) && defined(TEXTCOLOR)
  16. extern boolean colors_changed;    /* in tos.c */
  17. #endif
  18.  
  19. extern const char *roles[];    /* from u_init.c */
  20. extern char inv_order[];    /* from invent.c */
  21.  
  22. static boolean initial, from_file;
  23. static boolean NEARDATA set_order;
  24.  
  25. static void FDECL(nmcpy, (char *, const char *, int));
  26. static void FDECL(escapes, (const char *, char *));
  27. static void FDECL(rejectoption, (const char *));
  28. static void FDECL(badoption, (const char *));
  29. static char *FDECL(string_for_env_opt, (const char *, char *));
  30. static int FDECL(change_inv_order, (char *, int));
  31. static void FDECL(oc_to_str, (char *, char *));
  32.  
  33. static struct Bool_Opt
  34. {
  35.     const char *name;
  36.     boolean    *addr, initvalue;
  37. } boolopt[] = {
  38. #if defined(MICRO) && !defined(AMIGA)
  39.     {"BIOS", &flags.BIOS, FALSE},
  40. #endif
  41. #ifdef INSURANCE
  42.     {"checkpoint", &flags.ins_chkpt, TRUE},
  43. #endif
  44. #ifdef TEXTCOLOR
  45. # ifdef MICRO
  46.     {"color", &flags.use_color, TRUE},
  47. # else    /* systems that support multiple terminals, many monochrome */
  48.     {"color", &flags.use_color, FALSE},
  49. # endif
  50. #endif
  51.     {"confirm",&flags.confirm, TRUE},
  52. #ifdef TERMLIB
  53.     {"DECgraphics", &flags.DECgraphics, FALSE},
  54. #endif
  55.     {"disclose", &flags.end_disclose, TRUE},
  56.     {"female", &flags.female, FALSE},
  57.     {"fixinv", &flags.invlet_constant, TRUE},
  58. #ifdef AMIFLUSH
  59.     {"flush", &flags.amiflush, FALSE},
  60. #endif
  61.     {"help", &flags.help, TRUE},
  62. #ifdef TEXTCOLOR
  63.     {"hilite_pet", &flags.hilite_pet, FALSE},
  64. #endif
  65. #ifdef ASCIIGRAPH
  66.     {"IBMgraphics", &flags.IBMgraphics, FALSE},
  67. #endif
  68.     {"ignintr", &flags.ignintr, FALSE},
  69. #ifdef MAC_GRAPHICS_ENV
  70.     {"large_font", &flags.large_font, FALSE},
  71. #endif
  72.     {"legacy",&flags.legacy, TRUE},
  73.     {"lit_corridor", &flags.lit_corridor, FALSE},
  74. #ifdef MAC_GRAPHICS_ENV
  75.     {"MACgraphics", &flags.MACgraphics, TRUE},
  76. #endif
  77. #ifdef NEWS
  78.     {"news", &flags.news, TRUE},
  79. #endif
  80.     {"null", &flags.null, TRUE},
  81.     {"number_pad", &flags.num_pad, FALSE},
  82.     {"pickup", &flags.pickup, TRUE},
  83. #ifdef MAC
  84.     {"popup_dialog", &flags.popup_dialog, FALSE},
  85. #endif
  86. #if defined(MICRO) && !defined(AMIGA)
  87.     {"rawio", &flags.rawio, FALSE},
  88. #endif
  89.     {"rest_on_space", &flags.rest_on_space, FALSE},
  90.     {"safepet", &flags.safe_dog, TRUE},
  91. #ifdef EXP_ON_BOTL
  92.     {"showexp", &flags.showexp, FALSE},
  93. #endif
  94. #ifdef SCORE_ON_BOTL
  95.     {"showscore", &flags.showscore, FALSE},
  96. #endif
  97.     {"silent", &flags.silent, TRUE},
  98.     {"sortpack", &flags.sortpack, TRUE},
  99.     {"sound", &flags.soundok, TRUE},
  100.     {"standout", &flags.standout, FALSE},
  101.     {"time", &flags.time, FALSE},
  102.     {"tombstone",&flags.tombstone, TRUE},
  103.     {"verbose", &flags.verbose, TRUE},
  104.     {NULL, (boolean *)0, FALSE}
  105. };
  106.  
  107. static boolean need_redraw; /* for doset() */
  108.  
  109. void
  110. initoptions()
  111. {
  112.     register char *opts;
  113.     int i;
  114.  
  115.     for (i = 0; boolopt[i].name; i++) {
  116.         if (boolopt[i].addr)
  117.             *(boolopt[i].addr) = boolopt[i].initvalue;
  118.     }
  119.     flags.end_own = FALSE;
  120.     flags.end_top = 3;
  121.     flags.end_around = 2;
  122.     flags.msg_history = 20;
  123.  
  124.     /* Set the default monster and object class symbols.  Don't use */
  125.     /* memcpy() --- sizeof char != sizeof uchar on some machines.    */
  126.     for (i = 0; i < MAXOCLASSES; i++)
  127.             oc_syms[i] = (uchar) def_oc_syms[i];
  128.     for (i = 0; i < MAXMCLASSES; i++)
  129.             monsyms[i] = (uchar) def_monsyms[i];
  130.  
  131.     switch_graphics(ASCII_GRAPHICS);    /* set default characters */
  132. #ifdef UNIX
  133.     /*
  134.      * Set defaults for some options depending on what we can
  135.      * detect about the environment's capabilities.
  136.      * This has to be done after the global initialization above
  137.      * and before reading user-specific initialization via
  138.      * config file/environment variable below.
  139.      */
  140.     /* this detects the IBM-compatible console on most 386 boxes */
  141.     if (!strncmp(getenv("TERM"), "AT", 2)) {
  142.         switch_graphics(IBM_GRAPHICS);
  143. # ifdef TEXTCOLOR
  144.         flags.use_color = TRUE;
  145. # endif
  146.     }
  147. #endif /* UNIX */
  148. #if defined(UNIX) || defined(VMS)
  149.     /* detect whether a "vt" terminal can handle alternate charsets */
  150.     if (!strncmpi(getenv("TERM"), "vt", 2) && (AS && AE) &&
  151.         !strcmp(AS, "\016") && !strcmp(AE, "\017")) {
  152.         switch_graphics(DEC_GRAPHICS);
  153.     }
  154. #endif /* UNIX || VMS */
  155.  
  156. #ifdef MAC_GRAPHICS_ENV
  157.     switch_graphics(MAC_GRAPHICS);
  158. #endif /* MAC_GRAPHICS_ENV */
  159.  
  160. #ifdef TUTTI_FRUTTI
  161.     /* since this is done before init_objects(), do partial init here */
  162.     objects[SLIME_MOLD].oc_name_idx = SLIME_MOLD;
  163.     nmcpy(pl_fruit, OBJ_NAME(objects[SLIME_MOLD]), PL_FSIZ);
  164. #endif
  165.     opts = getenv("NETHACKOPTIONS");
  166.     if (!opts) opts = getenv("HACKOPTIONS");
  167.     if (opts)
  168.         if (*opts == '/' || *opts == '\\' || *opts == '@') {
  169.             if (*opts == '@') opts++;    /* @filename */
  170.             /* looks like a filename */
  171.             read_config_file(opts);
  172.         } else {
  173.             read_config_file(NULL);
  174.             parseoptions(opts, TRUE, FALSE);
  175.         }
  176.     else
  177.         read_config_file(NULL);
  178. #ifdef AMIGA
  179.     ami_wbench_init();    /* must be here or can't set fruit */
  180. #endif
  181. #ifdef TUTTI_FRUTTI
  182.     (void)fruitadd(pl_fruit);
  183.     /* Remove "slime mold" from list of object names; this will    */
  184.     /* prevent it from being wished unless it's actually present    */
  185.     /* as a named (or default) fruit.  Wishing for "fruit" will    */
  186.     /* result in the player's preferred fruit [better than "\033"].    */
  187.     obj_descr[SLIME_MOLD].oc_name = "fruit";
  188. #endif
  189.     if(flags.female)  {    /* should have been set in NETHACKOPTIONS */
  190.         roles[2] = "Cavewoman";
  191.         roles[6] = "Priestess";
  192.     }
  193. }
  194.  
  195. static void
  196. nmcpy(dest, src, maxlen)
  197.     char    *dest;
  198.     const char *src;
  199.     int    maxlen;
  200. {
  201.     int    count;
  202.  
  203.     for(count = 1; count < maxlen; count++) {
  204.         if(*src == ',' || *src == '\0') break; /*exit on \0 terminator*/
  205.         *dest++ = *src++;
  206.     }
  207.     *dest = 0;
  208. }
  209.  
  210. /*
  211.  * escapes: escape expansion for showsyms. C-style escapes understood include
  212.  * \n, \b, \t, \r, \xnnn (hex), \onnn (octal), \nnn (decimal). The ^-prefix
  213.  * for control characters is also understood, and \[mM] followed by any of the
  214.  * previous forms or by a character has the effect of 'meta'-ing the value (so
  215.  * that the alternate character set will be enabled).
  216.  */
  217. static void
  218. escapes(cp, tp)
  219. const char    *cp;
  220. char *tp;
  221. {
  222.     while (*cp)
  223.     {
  224.     int    cval = 0, meta = 0;
  225.  
  226.     if (*cp == '\\' && index("mM", cp[1])) {
  227.         meta = 1;
  228.         cp += 2;
  229.     }
  230.     if (*cp == '\\' && index("0123456789xXoO", cp[1]))
  231.     {
  232.         const char *dp, *hex = "00112233445566778899aAbBcCdDeEfF";
  233.         int dcount = 0;
  234.  
  235.         cp++;
  236.         if (*cp == 'x' || *cp == 'X')
  237.         for (++cp; (dp = index(hex, *cp)) && (dcount++ < 2); cp++)
  238.             cval = (cval * 16) + (dp - hex) / 2;
  239.         else if (*cp == 'o' || *cp == 'O')
  240.         for (++cp; (index("01234567",*cp)) && (dcount++ < 3); cp++)
  241.             cval = (cval * 8) + (*cp - '0');
  242.         else
  243.         for (; (index("0123456789",*cp)) && (dcount++ < 3); cp++)
  244.             cval = (cval * 10) + (*cp - '0');
  245.     }
  246.     else if (*cp == '\\')        /* C-style character escapes */
  247.     {
  248.         switch (*++cp)
  249.         {
  250.         case '\\': cval = '\\'; break;
  251.         case 'n': cval = '\n'; break;
  252.         case 't': cval = '\t'; break;
  253.         case 'b': cval = '\b'; break;
  254.         case 'r': cval = '\r'; break;
  255.         default: cval = *cp;
  256.         }
  257.         cp++;
  258.     }
  259.     else if (*cp == '^')        /* expand control-character syntax */
  260.     {
  261.         cval = (*++cp & 0x1f);
  262.         cp++;
  263.     }
  264.     else
  265.         cval = *cp++;
  266.     if (meta)
  267.         cval |= 0x80;
  268.     *tp++ = cval;
  269.     }
  270.     *tp = '\0';
  271. }
  272.  
  273. static void
  274. rejectoption(optname)
  275. const char *optname;
  276. {
  277. #ifdef MICRO
  278. # ifdef AMIGA
  279.     if(FromWBench){
  280.         pline("\"%s\" settable only from %s or in icon.",
  281.             optname, configfile);
  282.     } else
  283. # endif
  284.         pline("\"%s\" settable only from %s.", optname, configfile);
  285. #else
  286.     pline("%s can be set only from NETHACKOPTIONS or %s.", optname,
  287.             configfile);
  288. #endif
  289. }
  290.  
  291. static void
  292. badoption(opts)
  293. const char *opts;
  294. {
  295.     if(!initial) {
  296.         if(!strncmp(opts, "h", 1) || !strncmp(opts, "?", 1))
  297.         option_help();
  298.         else
  299.         pline("Unknown option: %s.  Enter \"?g\" for help.", opts);
  300.         return;
  301.     }
  302. # ifdef AMIGA
  303.     if(ami_wbench_badopt(opts)) {
  304. # endif
  305.     if(from_file)
  306.         raw_printf("Bad syntax in OPTIONS in %s: %s.", configfile, opts);
  307.     else
  308.         raw_printf("Bad syntax in NETHACKOPTIONS: %s.", opts);
  309. # ifdef AMIGA
  310.     }
  311. # endif
  312.     wait_synch();
  313. }
  314.  
  315. static char *
  316. string_for_env_opt(optname, opts)
  317. const char *optname;
  318. char *opts;
  319. {
  320.     register char *colon;
  321.  
  322.     if(!initial) {
  323.         rejectoption(optname);
  324.         return NULL;
  325.     }
  326.     colon = index(opts,':');
  327.     if(!colon) {
  328.         badoption(opts);
  329.         return NULL;
  330.     }
  331.     return ++colon;
  332. }
  333.  
  334. /*
  335.  * Change the inventory order, using the given string as the new order.
  336.  * Missing characters in the new order are filled in at the end from
  337.  * the current inv_order.
  338.  *
  339.  * This routine always returns 1 unless the parameter 'fail' is true
  340.  * and there is a duplicate or bad char in the string.
  341.  */
  342. static int
  343. change_inv_order(op, fail)
  344.     char *op;
  345.     int  fail;    /* If TRUE, return 0 if any duplicates or bad chars. */
  346. {
  347.     int oc_sym, num;
  348.     char *sp, *tmp, buf[BUFSZ];
  349.  
  350.     for (sp = op; *sp; sp++) {
  351.     oc_sym = def_char_to_objclass(*sp);
  352.  
  353.     /* Remove bad or duplicate entries. */
  354.     if (oc_sym == MAXOCLASSES ||
  355.         (!index(inv_order, oc_sym)) || (index(sp+1, *sp))) {
  356.  
  357.         if (fail) return 0;
  358.         for(tmp = sp; *tmp; tmp++)
  359.         tmp[0] = tmp[1];
  360.         sp--;
  361.     } else
  362.         *sp = (char) oc_sym;
  363.     } 
  364.     Strcpy(buf, op);
  365.     for (sp = inv_order, num = strlen(buf); *sp; sp++)
  366.     if (!index(buf, *sp))
  367.         buf[num++] = *sp;
  368.  
  369.     buf[num] = 0;
  370.     Strcpy(inv_order, buf);
  371.     return 1;
  372. }
  373.  
  374. void
  375. parseoptions(opts, tinitial, tfrom_file)
  376. register char *opts;
  377. boolean tinitial, tfrom_file;
  378. {
  379.     register char *op;
  380.     unsigned num;
  381.     boolean negated;
  382.     int i;
  383.  
  384.     initial = tinitial;
  385.     from_file = tfrom_file;
  386.     if ((op = index(opts, ',')) != 0) {
  387.         *op++ = 0;
  388.         parseoptions(op, initial, from_file);
  389.     }
  390.  
  391.     /* strip leading and trailing white space */
  392.     while (isspace(*opts)) opts++;
  393.     op = eos(opts);
  394.     while (--op >= opts && isspace(*op)) *op = '\0';
  395.  
  396.     if(!*opts) return;
  397.     negated = FALSE;
  398.     while((*opts == '!') || !strncmpi(opts, "no", 2)) {
  399.         if(*opts == '!') opts++; else opts += 2;
  400.         negated = !negated;
  401.     }
  402.     
  403. #if defined(MICRO) && !defined(AMIGA)
  404.     /* included for compatibility with old NetHack.cnf files */
  405.     if (!strncmp(opts, "IBM_", 4)) {
  406.         flags.BIOS = !negated;
  407.         return;
  408.     }
  409.  
  410.     /* put here cause it has to come from the config file */
  411.     if (!strncmpi(opts, "raw", 3)) {
  412.         if (initial)
  413.             flags.rawio = !negated;
  414.         else
  415.             rejectoption("rawio");
  416.         return;
  417.     }
  418. #endif /* MICRO */
  419.  
  420. #if defined(TOS) && defined(TEXTCOLOR)
  421.     if (!strncmpi(opts, "col", 3)) {
  422.         flags.use_color = !negated;
  423.         if (flags.BIOS && !initial) {
  424.             if (colors_changed)
  425.                 restore_colors();
  426.             else
  427.                 set_colors();
  428.         }
  429.     }
  430. #endif
  431.     /* other special-case boolean options */
  432. #ifdef TERMLIB
  433.     if (!strncmpi(opts, "DEC", 3)) {
  434. #ifdef REINCARNATION
  435.         if (!initial && Is_rogue_level(&u.uz))
  436.             assign_rogue_graphics(FALSE);
  437. #endif
  438.         flags.DECgraphics = !negated;
  439.         need_redraw = TRUE;
  440.         switch_graphics(flags.DECgraphics ?
  441.                 DEC_GRAPHICS : ASCII_GRAPHICS);
  442. #ifdef REINCARNATION
  443.         if (!initial && Is_rogue_level(&u.uz))
  444.             assign_rogue_graphics(TRUE);
  445. #endif
  446.         return;
  447.     }
  448. #endif /* TERMLIB */
  449. #ifdef ASCIIGRAPH
  450.     if (!strncmpi(opts, "IBMg", 4)) {
  451. #ifdef REINCARNATION
  452.         if (!initial && Is_rogue_level(&u.uz))
  453.             assign_rogue_graphics(FALSE);
  454. #endif
  455.         flags.IBMgraphics = !negated;
  456.         need_redraw = TRUE;
  457.         switch_graphics(flags.IBMgraphics ?
  458.                 IBM_GRAPHICS : ASCII_GRAPHICS);
  459. #ifdef REINCARNATION
  460.         if (!initial && Is_rogue_level(&u.uz))
  461.             assign_rogue_graphics(TRUE);
  462. #endif
  463.         return;
  464.     }
  465. #endif /* ASCIIGRAPH */
  466. #ifdef MAC_GRAPHICS_ENV
  467.     if (!strncmpi(opts, "MACg", 4)) {
  468. #ifdef REINCARNATION
  469.         if (!initial && Is_rogue_level(&u.uz))
  470.             assign_rogue_graphics(FALSE);
  471. #endif
  472.         flags.MACgraphics = !negated;
  473.         need_redraw = TRUE;
  474.         switch_graphics(flags.MACgraphics ?
  475.                 MAC_GRAPHICS : ASCII_GRAPHICS);
  476. #ifdef REINCARNATION
  477.         if (!initial && Is_rogue_level(&u.uz))
  478.             assign_rogue_graphics(TRUE);
  479. #endif
  480.         return;
  481.     }
  482. #endif /* MAC_GRAPHICS_ENV */
  483.  
  484.     /* common boolean options */
  485.  
  486.     if (!strncmpi(opts, "fem", 3)) {
  487.         if(!initial && flags.female == negated)
  488.             pline("That is not anatomically possible.");
  489.         else
  490.             flags.female = !negated;
  491.         return;
  492.     }
  493.  
  494.     if (!strncmpi(opts, "fix", 3)) {
  495.         flags.invlet_constant = !negated;
  496.         if (!initial && flags.invlet_constant) reassign();
  497.         return;
  498.     }
  499.  
  500.     if (!strncmpi(opts, "male", 4)) {
  501.         if(!initial && flags.female != negated)
  502.             pline("That is not anatomically possible.");
  503.         else
  504.             flags.female = negated;
  505.         return;
  506.     }
  507.  
  508.     if (!strncmpi(opts, "num", 3)) {
  509.         flags.num_pad = !negated;
  510.         if (!initial) number_pad(flags.num_pad ? 1 : 0);
  511.         return;
  512.     }
  513. #ifdef EXP_ON_BOTL
  514.     if (!strncmpi(opts, "showexp", 7)) {
  515.         flags.showexp = !negated;
  516.         flags.botl = 1;
  517.         return;
  518.     }
  519. #endif
  520. #ifdef SCORE_ON_BOTL
  521.     if (!strncmpi(opts, "showscore", 9)) {
  522.         flags.showscore = !negated;
  523.         flags.botl = 1;
  524.         return;
  525.     }
  526. #endif
  527.     if (!strncmpi(opts, "time", 4)) {
  528.         flags.time = !negated;
  529.         flags.botl = 1;
  530.         return;
  531.     }
  532.  
  533.     if (!strncmpi(opts, "legacy", 6)) {
  534.             if(!initial) rejectoption("legacy");
  535.         else flags.legacy = !negated;
  536.         return;
  537.     }
  538.  
  539.     /* compound options */
  540.  
  541.     if (!strncmpi(opts, "pet", 3)) {
  542.         if ((op = string_for_env_opt("pettype", opts)) != 0)
  543.             switch (*op) {
  544.             case 'd':    /* dog */
  545.             case 'D':
  546.                 preferred_pet = 'd';
  547.                 break;
  548.             case 'c':    /* cat */
  549.             case 'C':
  550.             case 'f':    /* feline */
  551.             case 'F':
  552.                 preferred_pet = 'c';
  553.                 break;
  554.             default:
  555.                 pline("Unrecognized pettype '%s'", op);
  556.                 break;
  557.             }
  558.         return;
  559.     }
  560.  
  561.     if (!strncmpi(opts, "cat", 3)) {
  562.         if ((op = string_for_env_opt("catname", opts)) != 0)
  563.             nmcpy(catname, op, 62);
  564.         return;
  565.     }
  566.  
  567.     if (!strncmpi(opts, "dog", 3)) {
  568.         if ((op = string_for_env_opt("dogname", opts)) != 0)
  569.             nmcpy(dogname, op, 62);
  570.         return;
  571.     }
  572.  
  573.     if (!strncmpi(opts, "msg", 3)) {
  574.         if ((op = string_for_env_opt("msghistory", opts)) != 0) {
  575.             flags.msg_history = atoi(op);
  576.         }
  577.         return;
  578.     }
  579. #ifdef TUTTI_FRUTTI
  580.     if (!strncmpi(opts, "fr", 2)) {
  581.         op = index(opts, ':');
  582.         if (!op) {
  583.             badoption(opts);
  584.             return;
  585.         }
  586.         op++;
  587.         if (!initial) {
  588.             struct fruit *f;
  589.             int numfruits = 0;
  590.  
  591.             for(f=ffruit; f; f=f->nextf) {
  592.             if (!strcmp(op, f->fname)) goto goodfruit;
  593.             numfruits++;
  594.             }
  595.             if (numfruits >= 100) {
  596.             pline("Doing that so many times isn't very fruitful.");
  597.             return;
  598.             }
  599.         }
  600. goodfruit:
  601.         nmcpy(pl_fruit, op, PL_FSIZ);
  602.         if (!*pl_fruit)
  603.             nmcpy(pl_fruit, OBJ_NAME(objects[SLIME_MOLD]), PL_FSIZ);
  604.         if (!initial)
  605.             (void)fruitadd(pl_fruit);
  606.         /* If initial, then initoptions is allowed to do it instead
  607.          * of here (initoptions always has to do it even if there's
  608.          * no fruit option at all.  Also, we don't want people
  609.          * setting multiple fruits in their options.)
  610.          */
  611.         return;
  612.     }
  613. #endif
  614.     /* graphics:string */
  615.     if (!strncmpi(opts, "gr", 2)) {
  616.         uchar translate[MAXPCHARS+1];
  617.         int lth;
  618.  
  619.         if (!(opts = string_for_env_opt("graphics", opts)))
  620.             return;
  621.         escapes(opts, opts);
  622.  
  623.         lth = strlen(opts);
  624.         if (lth > MAXPCHARS) lth = MAXPCHARS;
  625.         /* match the form obtained from PC configuration files */
  626.         for (i = 0; i < lth; i++)
  627.             translate[i] = (uchar) opts[i];
  628.         assign_graphics(translate, lth);
  629.         return;
  630.     }
  631.  
  632.     /* objects:string */
  633.     if (!strncmpi(opts, "objects", 7)) {
  634.         int k, length;
  635.  
  636.         if (!(opts = string_for_env_opt("objects", opts)))
  637.             return;
  638.         escapes(opts, opts);
  639.  
  640.         /*
  641.          * Override the default object class symbols.  The first
  642.          * object in the object class is the "random object".  I
  643.          * don't want to use 0 as an object class, so the "random
  644.          * object" is basically a place holder.
  645.          *
  646.          * The object class symbols have already been initialized in
  647.          * initoptions().
  648.          */
  649.         length = strlen(opts);
  650.         if (length >= MAXOCLASSES)
  651.             length = MAXOCLASSES-1;    /* don't count RANDOM_OBJECT */
  652.  
  653.         for (k = 0; k < length; k++)
  654.             oc_syms[k+1] = (uchar) opts[k];
  655.         return;
  656.     }
  657.  
  658.     /* monsters:string */
  659.     if (!strncmpi(opts, "monsters", 8)) {
  660.         int k, length;
  661.  
  662.         if (!(opts = string_for_env_opt("monsters", opts)))
  663.             return;
  664.         escapes(opts, opts);
  665.  
  666.         /* Override default mon class symbols set in initoptions(). */
  667.         length = strlen(opts);
  668.         if (length >= MAXMCLASSES)
  669.             length = MAXMCLASSES-1;    /* mon class 0 unused */
  670.  
  671.         for (k = 0; k < length; k++)
  672.             monsyms[k+1] = (uchar) opts[k];
  673.         return;
  674.     }
  675.  
  676.     /* name:string */
  677.     if (!strncmpi(opts, "name", 4)) {
  678.         if ((op = string_for_env_opt("name", opts)) != 0)
  679.             nmcpy(plname, op, (int)sizeof(plname)-1);
  680.         return;
  681.     }
  682.  
  683.     /* the order to list the pack */
  684.     if (!strncmpi(opts, "pack", 4)) {
  685.         op = index(opts,':');
  686.         if(!op) {
  687.             badoption(opts);
  688.             return;
  689.         }
  690.         op++;            /* skip : */
  691.  
  692.         if (!change_inv_order(op, 1))
  693.                 set_order = TRUE;
  694.         else
  695.                 badoption(opts);
  696.         return;
  697.     }
  698.  
  699.     /* scores:5t[op] 5a[round] o[wn] */
  700.     if (!strncmpi(opts, "scores", 6)) {
  701.         op = index(opts,':');
  702.         if(!op) {
  703.             badoption(opts);
  704.             return;
  705.         }
  706.         op++;
  707.         while(*op) {
  708.             num = 1;
  709.             if(digit(*op)) {
  710.                 num = atoi(op);
  711.                 while(digit(*op)) op++;
  712.             } else if(*op == '!') {
  713.                 negated = !negated;
  714.                 op++;
  715.             }
  716.             while(*op == ' ') op++;
  717.  
  718.             switch(*op) {
  719.                 case 't':
  720.                 case 'T':
  721.                     flags.end_top = num;
  722.                     break;
  723.                 case 'a':
  724.                 case 'A':
  725.                     flags.end_around = num;
  726.                     break;
  727.                 case 'o':
  728.                 case 'O':
  729.                     flags.end_own = !negated;
  730.                     break;
  731.                 default:
  732.                     badoption(opts);
  733.                     return;
  734.             }
  735.             while(letter(*++op) || *op == ' ') ;
  736.             if(*op == '/') op++;
  737.         }
  738.         return;
  739.     }
  740.     if (!strncmpi(opts, "win", 3)) {
  741.         if ((op = string_for_env_opt("windowtype", opts)) != 0) {
  742.         char buf[16];
  743.         nmcpy(buf, op, 15);
  744.         choose_windows(buf);
  745.         }
  746.         return;
  747.     }
  748.  
  749.     /* OK, if we still haven't recognized the option, check the boolean
  750.      * options list
  751.      */
  752.     for (i = 0; boolopt[i].name; i++) {
  753.         if (boolopt[i].addr && !strncmpi(boolopt[i].name, opts, 3)) {
  754.             *(boolopt[i].addr) = !negated;
  755. #ifdef TEXTCOLOR
  756.             if((boolopt[i].addr) == &flags.use_color)
  757.                 need_redraw = TRUE;
  758.  
  759.             if((boolopt[i].addr) == &flags.hilite_pet)
  760.                 need_redraw = TRUE;
  761. #endif
  762.             if (!initial && boolopt[i].addr==&flags.lit_corridor) {
  763.                 /*
  764.                  * All corridor squares seen via night vision or
  765.                  * candles & lamps change.  Update them by calling
  766.                  * newsym() on them.  Don't do this if we are
  767.                  * initializing the options --- the vision system
  768.                  * isn't set up yet.
  769.                  */
  770.                 vision_recalc(2);        /* shut down vision */
  771.                 vision_full_recalc = 1;    /* delayed recalc */
  772.             }
  773.             return;
  774.         }
  775.     }
  776.  
  777.     /* out of valid options */
  778.     badoption(opts);
  779. }
  780.  
  781. /*
  782.  * Convert the given string of object classes to a string of default object
  783.  * symbols.
  784.  */
  785. static void
  786. oc_to_str(src,dest)
  787.     char *src, *dest;
  788. {
  789.     int i;
  790.  
  791.     while ((i = (int) *src++) != 0) {
  792.     if (i < 0 || i >= MAXOCLASSES)
  793.         impossible("oc_to_str:  illegal object class %d", i);
  794.     else
  795.         *dest++ = def_oc_syms[i];
  796.     }
  797.     *dest = '\0';
  798. }
  799.  
  800. #ifdef MICRO
  801. # define OPTIONS_HEADING "OPTIONS"
  802. #else
  803. # define OPTIONS_HEADING "NETHACKOPTIONS"
  804. #endif
  805.  
  806. int
  807. doset()
  808. {
  809.     char buf[BUFSZ], pack_order[MAXOCLASSES+1], on_off;
  810.     const char *opt_name;
  811.     int i;
  812.     winid tmpwin;
  813.  
  814.     switch (yn_function("Show the current settings [c], or set options [s]?",
  815.                 "csq", 'q')) {
  816.     default:
  817.     case 'q':
  818.         clear_nhwindow(WIN_MESSAGE);
  819.         return 0;
  820.     case 'c':
  821.         tmpwin = create_nhwindow(NHW_MENU);
  822.         putstr(tmpwin, 0, OPTIONS_HEADING);
  823.         putstr(tmpwin, 0, "");
  824.         /* print the booleans */
  825.         for (i = 0; boolopt[i].name; i++) {
  826.         if (!boolopt[i].addr) continue;
  827.         opt_name = boolopt[i].name;
  828.         if (*(boolopt[i].addr)) {
  829.             on_off = ' ';               /* on */
  830.         } else {
  831.             if (!strcmp(opt_name, "female"))
  832.             opt_name = "male",  on_off = ' ';
  833.             else
  834.             on_off = '!';           /* off */
  835.         }
  836.         Sprintf(buf, "%c%s", on_off, opt_name);
  837.         putstr(tmpwin, 0, buf);
  838.         }
  839.         /* print the compounds */
  840.         Sprintf(buf, " catname: %s",
  841.             (catname[0] != 0) ? catname : "(null)");
  842.         putstr(tmpwin, 0, buf);
  843.         Sprintf(buf, " dogname: %s",
  844.             (dogname[0] != 0) ? dogname : "(null)");
  845.         putstr(tmpwin, 0, buf);
  846. #ifdef TUTTI_FRUTTI
  847.         Sprintf(buf, " fruit: %s", pl_fruit);
  848.         putstr(tmpwin, 0, buf);
  849. #endif
  850.         Sprintf(buf, " msghistory: %u", flags.msg_history);
  851.         putstr(tmpwin, 0, buf);
  852.         Sprintf(buf, " name: %s", plname);
  853.         putstr(tmpwin, 0, buf);
  854.         oc_to_str(inv_order, pack_order);
  855.         Sprintf(buf, " packorder: %s", pack_order);
  856.         putstr(tmpwin, 0, buf);
  857.         Sprintf(buf, " pettype: %s", preferred_pet == 'c' ? "cat" :
  858.                     preferred_pet == 'd' ? "dog" : "random");
  859.         putstr(tmpwin, 0, buf);
  860.         Sprintf(buf, " scores: %utop/%uaround%s",
  861.                 flags.end_top, flags.end_around,
  862.                 (flags.end_own ? "/own" : ""));
  863.         putstr(tmpwin, 0, buf);
  864.         Sprintf(buf, " windowtype: %s", windowprocs.name);
  865.         putstr(tmpwin, 0, buf);
  866.         display_nhwindow(tmpwin, TRUE);
  867.         destroy_nhwindow(tmpwin);
  868.         break;
  869.     case 's':
  870.         clear_nhwindow(WIN_MESSAGE);
  871.         getlin("What options do you want to set?", buf);
  872.         clear_nhwindow(WIN_MESSAGE);
  873.         if(buf[0] == '\033') return 0;
  874.         need_redraw = FALSE;
  875.         parseoptions(buf, FALSE, FALSE);
  876.         if(need_redraw)
  877.         (void) doredraw();
  878.         break;
  879.     }
  880.  
  881.     return 0;
  882. }
  883.  
  884. int
  885. dotogglepickup() {
  886.     flags.pickup = !flags.pickup;
  887.     pline("Pickup: %s.", flags.pickup ? "ON" : "OFF");
  888.     return 0;
  889. }
  890.  
  891. /* data for option_help() */
  892. static const char *opt_intro[] = {
  893.     "",
  894.     "                 NetHack Options Help:",
  895.     "",
  896. #define CONFIG_SLOT 3    /* fill in next value at run-time */
  897.     NULL,
  898. #ifndef MICRO
  899.     "or use `NETHACKOPTIONS=\"<options>\"' in your environment;",
  900. # ifdef VMS
  901.     "-- for example, $ DEFINE NETHACKOPTIONS \"nopickup,fruit:kumquat\"",
  902. # endif
  903. #endif
  904.     "or press \"O\" while playing, and type your <options> at the prompt.",
  905.     "In either case, <options> is a list of options separated by commas.",
  906.     "",
  907.  "Boolean options (which can be negated by prefixing them with '!' or \"no\"):",
  908.     NULL
  909. };
  910. static const char *opt_compound[] = {
  911.     "Compound options:",
  912.     "`catname'   - the name of your (first) cat (e.g., catname:Tabby),",
  913.     "`dogname'   - the name of your (first) dog (e.g., dogname:Fang),",
  914. #ifdef TUTTI_FRUTTI
  915.     "`fruit'     - the name of a fruit you enjoy eating,",
  916. # define FRUIT_OFFSET 1
  917. #else
  918. # define FRUIT_OFFSET 0
  919. #endif
  920.     "`graphics'  - defines the symbols to use in drawing the dungeon map,",
  921.     "`monsters'  - defines the symbols to use for monsters,",
  922.     "`msghistory'- number of top line messages to save,",
  923.     "`name'      - your character's name (e.g., name:Merlin-W),",
  924.     "`objects'   - defines the symbols to use for objects,",
  925.     "`packorder' - the inventory order of the items in your pack",
  926. #define PCKORD_SLOT 9+FRUIT_OFFSET
  927.     NULL,
  928.     "`pettype'   - your preferred initial pet type,",
  929.     "`scores'    - the parts of the score list you wish to see,",
  930.     "`windowtype'- windowing system to use.",
  931.     "",
  932.  "Some of the options can be set only before the game is started.  You will",
  933.     "be so informed, if you attempt to set them while in the game.",
  934.     NULL
  935. };
  936.  
  937. void
  938. option_help()
  939. {
  940.     char    buf[BUFSZ], pack_order[MAXOCLASSES+1];
  941.     register int    i;
  942.     winid datawin;
  943.  
  944.     datawin = create_nhwindow(NHW_TEXT);
  945. #ifdef AMIGA
  946.     if(FromWBench){
  947.     Sprintf(buf,"Set options as OPTIONS= in %s or in icon;",configfile);
  948.     } else
  949. #endif
  950.     Sprintf(buf, "Set options as OPTIONS=<options> in %s;", configfile);
  951.     opt_intro[CONFIG_SLOT] = (const char *) buf;
  952.     for (i = 0; opt_intro[i]; i++)
  953.     putstr(datawin, 0, opt_intro[i]);
  954.  
  955.     /* Boolean options */
  956.     for (i = 0; boolopt[i].name; i++) {
  957.     if (boolopt[i].addr)
  958.         next_opt(datawin, boolopt[i].name);
  959.     }
  960.     next_opt(datawin, "");
  961.  
  962.     /* Compound options */
  963.     oc_to_str(inv_order, pack_order);
  964.     Sprintf(buf, "              (currently, packorder:%s ),", pack_order);
  965. #if 0
  966.     assert( opt_compound[PCKORD_SLOT] == NULL );
  967. #endif
  968.     opt_compound[PCKORD_SLOT] = (const char *) buf;
  969.     for (i = 0; opt_compound[i]; i++)
  970.     putstr(datawin, 0, opt_compound[i]);
  971.  
  972.     display_nhwindow(datawin, FALSE);
  973.     destroy_nhwindow(datawin);
  974.     return;
  975. }
  976.  
  977. /*
  978.  * prints the next boolean option, on the same line if possible, on a new
  979.  * line if not. End with next_opt("").
  980.  */
  981. void
  982. next_opt(datawin, str)
  983. winid datawin;
  984. const char *str;
  985. {
  986.     static char buf[121];
  987.     int i;
  988.     char *s;
  989.  
  990.     if (!*str) {
  991.         for (s = buf; *s; s++);    /* find end of string */
  992.         if (s > &buf[1] && s[-2] == ',')
  993.             s[-2] = 0;    /* strip last ", " */
  994.         i = 121;
  995.     }
  996.     else    
  997.         i = strlen(buf) + strlen(str) + 2;
  998.  
  999.     if (i > COLNO - 2) { /* rule of thumb */
  1000.         putstr(datawin, 0, buf);
  1001.         buf[0] = 0;
  1002.     }
  1003.     if (*str) {
  1004.         Strcat(buf, str);
  1005.         Strcat(buf, ", ");
  1006.     }
  1007.     else
  1008.         putstr(datawin, 0, str);
  1009.     return;
  1010. }
  1011.  
  1012. #ifdef TUTTI_FRUTTI
  1013. /* Returns the fid of the fruit type; if that type already exists, it
  1014.  * returns the fid of that one; if it does not exist, it adds a new fruit
  1015.  * type to the chain and returns the new one.
  1016.  */
  1017. int
  1018. fruitadd(str)
  1019. char *str;
  1020. {
  1021.     register int i,j;
  1022.     register struct fruit *f;
  1023. #ifdef GCC_WARN
  1024.     struct fruit *lastf = (struct fruit *)0;
  1025. #else
  1026.     struct fruit *lastf;
  1027. #endif
  1028.     int highest_fruit_id = 0;
  1029.     char buf[PL_FSIZ];
  1030.     boolean user_specified = (str == pl_fruit);
  1031.     /* if not user-specified, then it's a fruit name for a fruit on
  1032.      * a bones level...
  1033.      */
  1034.  
  1035.     /* Note: every fruit has an id (spe for fruit objects) of at least
  1036.      * 1; 0 is an error.
  1037.      */
  1038.     if (user_specified) {
  1039.         /* disallow naming after other foods (since it'd be impossible
  1040.          * to tell the difference)
  1041.          */
  1042.  
  1043.         boolean found = FALSE;
  1044.  
  1045.         for(i = bases[j=letindex(FOOD_CLASS)]; i < bases[j+1]; i++) {
  1046.             if (!strcmp(OBJ_NAME(objects[i]), pl_fruit)) {
  1047.                 found = TRUE;
  1048.                 break;
  1049.             }
  1050.         }
  1051.         if (found ||
  1052.             (!strncmp(str, "tin of ", 7) && name_to_mon(str+7) > -1) ||
  1053.             !strcmp(str, "empty tin") ||
  1054.             !strcmp(str, "tin of spinach") ||
  1055.             ((!strncmp(eos(str)-6," corpse",7) ||
  1056.                         !strncmp(eos(str)-3, " egg",4))
  1057.             && name_to_mon(str) > -1))
  1058.             {
  1059.                 Strcpy(buf, pl_fruit);
  1060.                 Strcpy(pl_fruit, "candied ");
  1061.                 nmcpy(pl_fruit+8, buf, PL_FSIZ-8);
  1062.         }
  1063.     }
  1064.     for(f=ffruit; f; f = f->nextf) {
  1065.         lastf = f;
  1066.         if(f->fid > highest_fruit_id) highest_fruit_id = f->fid;
  1067.         if(!strncmp(str, f->fname, PL_FSIZ))
  1068.             goto nonew;
  1069.     }
  1070.     /* if adding another fruit would overflow spe, use a random
  1071.        fruit instead... we've got a lot to choose from. */
  1072.     if (highest_fruit_id >= 127) return rnd(127);
  1073.     highest_fruit_id++;
  1074.     f = newfruit();
  1075.     if (ffruit) lastf->nextf = f;
  1076.     else ffruit = f;
  1077.     Strcpy(f->fname, str);
  1078.     f->fid = highest_fruit_id;
  1079.     f->nextf = 0;
  1080. nonew:
  1081.     if (user_specified) current_fruit = highest_fruit_id;
  1082.     return f->fid;
  1083. }
  1084. #endif
  1085.  
  1086. /*options.c*/
  1087.